#pragma once

#include <iostream>
#include <sys/select.h>
#include "Log.hpp"
#include "Socket.hpp"

class SelectServer
{
public:
    const static uint16_t default_port = 8080;
    const static int default_fd = -1;
    const static int fd_max_num = FD_SETSIZE; // 定义变量存储，方便修改

    SelectServer(uint16_t port = default_port)
        : _port(port)
    {
        for (int i = 0; i < fd_max_num; ++i)
        {
            _fd_array[i] = default_fd;
        }
    }

    void Initial()
    {
        _listensock.CreateSock();
        _listensock.Bind(_port);
        _listensock.Listen();
        lg(Info, "create listen sock success, fd: %d", _listensock.GetSockFd());
    }

    void Accept()
    {
        std::string clientip;
        uint16_t clientport;
        int fd = _listensock.Accept(&clientip, &clientport);
        // 当Accept失败时，不再执行下面的操作
        if (fd < 0)
            return;
        lg(Info, "accept success, [%s:%d] fd: %d", clientip.c_str(), clientport, fd); // ip与端口号顺便输出
        int pos = 0;                                                                  // 写成pos更标准些
        for (; pos < fd_max_num; ++pos)
        {
            if (_fd_array[pos] != default_fd)
                continue;
            else // 直接退出，在下面再做处理
                break;
        }
        if (pos == fd_max_num)
        {
            lg(Warning, "select fd array is full, fd: %d close", fd);
            close(fd);
        }
        else
        {
            _fd_array[pos] = fd;
            PrintAllFd();
        }
    }

    void Receive(int fd, int pos)
    {
        char buffer[1024];
        ssize_t sz = read(fd, buffer, sizeof(buffer) - 1);
        if (sz > 0)
        {
            buffer[sz] = 0;
            std::cout << "server get a message: " << buffer << std::endl;
        }
        else if (sz == 0)
        {
            // 只是输出消息，并不是错误
            lg(Info, "client quit, fd: %d close", fd);
            close(fd); // 不需要使用时，别忘了关闭
            _fd_array[pos] = default_fd;
        }
        else
        {
            lg(Error, "read error, fd: %d, errno: %d, error string: %s", fd, errno, strerror(errno)); // fd顺便也输出
            close(fd);
            _fd_array[pos] = default_fd;
        }
    }

    // void Receive(int pos, fd_set &rfds)
    // {
    //     char buffer[1024];
    //     ssize_t sz = read(_fd_array[pos], buffer, sizeof(buffer) - 1);
    //     if (sz > 0)
    //     {
    //         buffer[sz] = 0;
    //         std::cout << "server get a message: " << buffer << std::endl;
    //     }
    //     else if (sz == 0)
    //     {
    //         lg(Error, "client quit, fd: %d close", _fd_array[pos]);
    //         FD_CLR(_fd_array[pos], &rfds);
    //         _fd_array[pos] = default_fd;
    //     }
    //     else
    //     {
    //         lg(Error, "read error, errno: %d, error string: %s", errno, strerror(errno));
    //         FD_CLR(_fd_array[pos], &rfds);
    //         _fd_array[pos] = default_fd;
    //     }
    // }

    void Dispatch(fd_set &rfds)
    {
        for (int i = 0; i < fd_max_num; ++i)
        {
            // int listensock = _listensock.GetSockFd();        // 只用一次，无需单独设置一个变量
            int fd = _fd_array[i]; // 需多次使用，可用变量保存
            if (fd == default_fd)
                continue;
            if (FD_ISSET(fd, &rfds))
            {
                if (fd == _listensock.GetSockFd())
                    Accept();
                else
                    Receive(fd, i); // 无需修改rfds
                // Receive(i, rfds);
            }
        }
    }

    void Start()
    {
        _fd_array[0] = _listensock.GetSockFd();
        for (;;)
        {
            int maxfd = _fd_array[0];
            struct timeval timeout = {3, 0};
            // struct timeval timeout = {0, 0}; // 不能在下面的select中直接传0（阻塞等待），需将timeval变量设置为0后再传入，否则
            fd_set rfds;
            FD_ZERO(&rfds);
            FD_SET(_fd_array[0], &rfds);         // 别忘了将其加入select的rfds中，否则会一直timeout
            for (int i = 0; i < fd_max_num; ++i) // 可从0开始
            {
                if (_fd_array[i] == default_fd)
                    continue;
                FD_SET(_fd_array[i], &rfds);
                if (_fd_array[i] > maxfd)
                {
                    maxfd = _fd_array[i];
                    lg(Info, "update max fd: %d", maxfd); // 更新消息顺便输出
                }
            }
            int n = select(maxfd + 1, &rfds, nullptr, nullptr, &timeout /*nullptr*/);
            // std::cout << n << std::endl;
            switch (n)
            {
            case 0:
                // lg(Info, "timeout");
                std::cout << "time out, timeout: " << timeout.tv_sec << "." << timeout.tv_usec << std::endl;
                break;
            case -1:
                lg(Error, "select error");
                break;
            default:
                lg(Info, "select waite success, ready num: %d", n);
                Dispatch(rfds);
                break;
            }
        }
    }

    void PrintAllFd()
    {
        std::cout << "online fd: ";
        for (int i = 0; i < fd_max_num; ++i)
        {
            if (_fd_array[i] == default_fd)
                continue;
            std::cout << _fd_array[i] << " ";
        }
        std::cout << std::endl;
    }

    ~SelectServer()
    {
        _listensock.Close();
    }

private:
    Socket _listensock;
    uint16_t _port;
    int _fd_array[fd_max_num];
};